/*
 * Copyright (C) 2023 Coder.AN
 * Email: an.hongjun@foxmail.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
#include "xbox.h"


XBox::XBox(void (*callback_func)(struct XBoxInputVal), const char* dev_name)
{
    xboxDev = open(dev_name, O_RDONLY);
    if (xboxDev < 0) {
        std::cout << "Open Xbox Err." << std::endl;
        exit(-1);
    }
    memset(&xboxInputValue, 0, sizeof(xboxInputValue));
    callback = callback_func;

    work_handle = true;
    work_thread = std::thread(&XBox::work_func, this);
}

XBox::~XBox()
{
    work_handle = false;
    work_thread.join();
    if (xboxDev > 0) {
        close(xboxDev);
    }
}

void XBox::work_func()
{
    struct input_event ev;
    while (work_handle)
    {
        int bytes = read(xboxDev, &ev, sizeof(ev));
        if (bytes != sizeof(ev)) {
            continue;
        }
        switch (ev.type) {
            case EV_KEY:
                parseKey(ev);
                break;
            case EV_ABS:
                parseAbs(ev);
                break;
            case EV_SYN:
                parseSyn(ev);
                break;
            default:
                break;
        }
    }
}

void XBox::parseSyn(struct input_event ev)
{
    switch(ev.code) {
        case SYN_REPORT:
            callback(xboxInputValue);
            break;
        case SYN_CONFIG:
            printf("SYN_CONFIG\n");
            break;
        case SYN_MT_REPORT:
            printf("SYN_MT_REPORT\n");
            break;
        case SYN_DROPPED:
            printf("SYN_DROPPED\n");
            break;
        case SYN_MAX:
            printf("SYN_MAX\n");
            break;
        case SYN_CNT:
            printf("SYN_CNT\n");
            break;
        default:
            printf("syn default\n");
    }
}

void XBox::parseKey(struct input_event ev)
{
    switch (ev.code) {
        case BTN_A:
            xboxInputValue.A = ev.value;
            break;
        case BTN_B:
            xboxInputValue.B = ev.value;
            break;
        case BTN_X:
            xboxInputValue.X = ev.value;
            break;
        case BTN_Y:
            xboxInputValue.Y = ev.value;
            break;
        case BTN_TL:
            xboxInputValue.lb = ev.value;
            break;
        case BTN_TR:
            xboxInputValue.rb = ev.value;
            break;
        case BTN_MODE:
            xboxInputValue.guide = ev.value;
            break;
        case BTN_SELECT:
            xboxInputValue.back = ev.value;
            break;
        case BTN_START:
            xboxInputValue.start = ev.value;
            break;
        default:
            printf("type: %x value:%d\n", ev.code, ev.value);
            break;
    }
}

void XBox::parseAbs(struct input_event ev)
{
    switch (ev.code) {
        case ABS_X:
            xboxInputValue.X1 = ev.value;
            break;
        case ABS_Y:
            xboxInputValue.Y1 = ev.value;
            break;
        case ABS_Z:
            xboxInputValue.lt = ev.value;
            break;
        case ABS_RX:
            xboxInputValue.X2 = ev.value;
            break;
        case ABS_RY:
            xboxInputValue.Y2 = ev.value;
            break;
        case ABS_RZ:
            xboxInputValue.rt = ev.value;
            break;
        case ABS_HAT0X:
            if (ev.value == -1) {
                xboxInputValue.dl = 1;
            }
            else if(ev.value == 1) {
                xboxInputValue.dr = 1;
            }
            else {
                xboxInputValue.dl = 0;
                xboxInputValue.dr = 0;
            }
            break;
        case ABS_HAT0Y:
            if (ev.value == -1) {
                xboxInputValue.du = 1;
            }
            else if(ev.value == 1) {
                xboxInputValue.dd = 1;
            }
            else {
                xboxInputValue.du = 0;
                xboxInputValue.dd = 0;
            }
            break;
        default:
            printf("type: %x value:%d\n", ev.code, ev.value);
            break;
    }
}
